package cn.zyjblogs.sms.provider.aliyun;

import cn.zyjblogs.sms.common.SmsRequest;
import cn.zyjblogs.sms.common.SmsResponse;
import cn.zyjblogs.sms.provider.AbstractSmsGranter;
import cn.zyjblogs.sms.provider.SmsGranter;
import cn.zyjblogs.sms.task.constant.ProviderType;
import cn.zyjblogs.sms.task.constant.SendStatus;
import cn.zyjblogs.sms.task.constant.TaskStatus;
import cn.zyjblogs.sms.task.dto.TaskResultDTO;
import cn.zyjblogs.starter.common.utils.collect.Lists;
import com.alibaba.fastjson2.JSON;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.dysmsapi20170525.AsyncClient;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsResponseBody;
import com.baidubce.services.sms.model.SendMessageItem;
import com.baidubce.services.sms.model.SendMessageV3Response;
import darabonba.core.client.ClientOverrideConfiguration;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

/**
 * @author zhuyijun
 */
@Slf4j
public class AliSmsHandleGranter extends AbstractSmsGranter {
    private static final ProviderType PROVIDER_TYPE = ProviderType.ALI;
    public AliSmsHandleGranter() {
        super(PROVIDER_TYPE);
    }
    public AliSmsHandleGranter(ProviderType providerType) {
        super(providerType);
    }

    private static final Map<String, String> ERROR_CODE_MAP = new HashMap<>(150);

    static {
        ERROR_CODE_MAP.put("OK", "OK");
        ERROR_CODE_MAP.put("isv.SMS_SIGNATURE_SCENE_ILLEGAL", "签名和模板类型不一致");
        ERROR_CODE_MAP.put("isv.EXTEND_CODE_ERROR", "扩展码使用错误，相同的扩展码不可用于多个签名");
        ERROR_CODE_MAP.put("isv.DOMESTIC_NUMBER_NOT_SUPPORTED", "国际/港澳台消息模板不支持发送境内号码");
        ERROR_CODE_MAP.put("isv.DENY_IP_RANGE", "源IP地址所在的地区被禁用");
        ERROR_CODE_MAP.put("isv.DAY_LIMIT_CONTROL", "触发日发送限额");
        ERROR_CODE_MAP.put("isv.SMS_CONTENT_ILLEGAL", "短信内容包含禁止发送内容");
        ERROR_CODE_MAP.put("isv.SMS_SIGN_ILLEGAL", "签名禁止使用");
        ERROR_CODE_MAP.put("isp.RAM_PERMISSION_DENY", "RAM权限不足");
        ERROR_CODE_MAP.put("isv.OUT_OF_SERVICE", "业务停机");
        ERROR_CODE_MAP.put("isv.PRODUCT_UN_SUBSCRIPT", "未开通云通信产品的阿里云客户");
        ERROR_CODE_MAP.put("isv.PRODUCT_UNSUBSCRIBE", "产品未开通");
        ERROR_CODE_MAP.put("isv.ACCOUNT_NOT_EXISTS", "账户不存在");
        ERROR_CODE_MAP.put("isv.ACCOUNT_ABNORMAL", "账户异常");
        ERROR_CODE_MAP.put("isv.SMS_TEMPLATE_ILLEGAL", "该账号下找不到对应模板");
        ERROR_CODE_MAP.put("isv.SMS_SIGNATURE_ILLEGAL", "该账号下找不到对应签名");
        ERROR_CODE_MAP.put("isv.INVALID_PARAMETERS", "参数格式不正确");
        ERROR_CODE_MAP.put("isp.SYSTEM_ERROR", "系统出现错误，请重新调用");
        ERROR_CODE_MAP.put("isv.MOBILE_NUMBER_ILLEGAL", "手机号码格式错误");
        ERROR_CODE_MAP.put("isv.MOBILE_COUNT_OVER_LIMIT", "手机号码数量超过限制，最多支持1000条");
        ERROR_CODE_MAP.put("isv.TEMPLATE_MISSING_PARAMETERS", "模板变量中存在未赋值变量");
        ERROR_CODE_MAP.put("isv.BUSINESS_LIMIT_CONTROL", "触发云通信流控限制");
        ERROR_CODE_MAP.put("isv.INVALID_JSON_PARAM", "参数格式错误，请修改为字符串值");
        ERROR_CODE_MAP.put("isv.BLACK_KEY_CONTROL_LIMIT", "变量中传入疑似违规信息");
        ERROR_CODE_MAP.put("isv.PARAM_LENGTH_LIMIT", "变量中传入疑似违规信息");
        ERROR_CODE_MAP.put("isv.PARAM_NOT_SUPPORT_URL", "变量不支持传入URL");
        ERROR_CODE_MAP.put("isv.AMOUNT_NOT_ENOUGH", "账户余额不足");
        ERROR_CODE_MAP.put("isv.TEMPLATE_PARAMS_ILLEGAL", "传入的变量内容和实际申请模板时变量所选择的属性类型不配");
        ERROR_CODE_MAP.put("SignatureDoesNotMatch", "客户端生成的签名与服务端不匹配");
        ERROR_CODE_MAP.put("InvalidTimeStamp.Expired", "时间戳或日期已过期");
        ERROR_CODE_MAP.put("SignatureNonceUsed", "签名随机数已被使用");
        ERROR_CODE_MAP.put("InvalidVersion", "API版本号错误");
        ERROR_CODE_MAP.put("InvalidAction.NotFound", "未找到指定的API，请检查您的URL和方法");
        ERROR_CODE_MAP.put("isv.SIGN_COUNT_OVER_LIMIT", "超过单自然日签名申请数量上限");
        ERROR_CODE_MAP.put("isv.TEMPLATE_COUNT_OVER_LIMIT", "超过单自然日模板申请数量上限");
        ERROR_CODE_MAP.put("isv.SIGN_NAME_ILLEGAL", "签名名称不符合规范");
        ERROR_CODE_MAP.put("isv.SIGN_FILE_LIMIT", "签名认证材料附件大小超过限制");
        ERROR_CODE_MAP.put("isv.SIGN_OVER_LIMIT", "签名字符数量超过限制");
        ERROR_CODE_MAP.put("isv.TEMPLATE_OVER_LIMIT", "模板字符数量超过限制");
        ERROR_CODE_MAP.put("SIGNATURE_BLACKLIST", "签名内容涉及违规信息");
        ERROR_CODE_MAP.put("isv.SHORTURL_OVER_LIMIT", "超过单自然日短链申请数量上限");
        ERROR_CODE_MAP.put("isv.NO_AVAILABLE_SHORT_URL", "该账号无有效短链");
        ERROR_CODE_MAP.put("isv.SHORTURL_NAME_ILLEGAL", "短链名不能超过13字符");
        ERROR_CODE_MAP.put("isv.SOURCEURL_OVER_LIMIT", "原始链接字符数量超过限制");
        ERROR_CODE_MAP.put("isv.SHORTURL_TIME_ILLEGAL", "短链有效期期限超过限制");
        ERROR_CODE_MAP.put("isv.PHONENUMBERS_OVER_LIMIT", "上传手机号个数超过上限");
        ERROR_CODE_MAP.put("isv.SHORTURL_STILL_AVAILABLE", "原始链接生成的短链仍在有效期内");
        ERROR_CODE_MAP.put("isv.ERROR_EMPTY_FILE", "签名文件为空");
        ERROR_CODE_MAP.put("isp.GATEWAY_ERROR", "调用发送应用模块失败");
        ERROR_CODE_MAP.put("isv.ERROR_SIGN_NOT_DELETE", "审核中的签名，暂时无法删除");
        ERROR_CODE_MAP.put("isv.ERROR_SIGN_NOT_MODIFY", "已通过的签名不支持修改");
        ERROR_CODE_MAP.put("isv.ERROR_TEMPLATE_NOT_DELETE", "审核中的模板，暂时无法删除");
        ERROR_CODE_MAP.put("isv.ERROR_TEMPLATE_NOT_MODIFY", "已通过的模板不支持修改");
        ERROR_CODE_MAP.put("isv.SMS_OVER_LIMIT", "单日最多申请模板或签名100条");
        ERROR_CODE_MAP.put("isv.CUSTOMER_REFUSED", "用户已退订推广短信");
        ERROR_CODE_MAP.put("isv.SMS_TEST_SIGN_TEMPLATE_LIMIT", "测试模板和签名限制");
        ERROR_CODE_MAP.put("isv.SHORTURL_DOMAIN_EMPTY", "短链创建失败");
        ERROR_CODE_MAP.put("template_parameter_count_illegal", "验证码模板仅支持一个验证码作为变量");
        ERROR_CODE_MAP.put("isv.SMS_TEST_TEMPLATE_PARAMS_ILLEGAL", "测试专用模板中的变量仅支持4~6位纯数字");
        ERROR_CODE_MAP.put("isv.SMS_TEST_NUMBER_LIMIT", "只能向已回复授权信息的手机号发送");
        ERROR_CODE_MAP.put("isv.SMS_SIGN_EMOJI_ILLEGAL", "签名不能包含emoji表情");
        ERROR_CODE_MAP.put("isv.SECURITY_FROZEN_ACCOUNT", "因该账号长时间未使用，出于对您的账号安全考虑，已限制您账号的短信发送");
        ERROR_CODE_MAP.put("IS_CLOSE", "短信下发时通道被关停");
        ERROR_CODE_MAP.put("MOBILE_NOT_ON_SERVICE", "停机、空号、暂停服务、关机、不在服务区");
        ERROR_CODE_MAP.put("MOBILE_SEND_LIMIT", "单个号码日、月发送上限，流控超限，频繁发送超限");
        ERROR_CODE_MAP.put("MOBILE_ACCOUNT_ABNORMAL", "用户账户异常、携号转网、欠费等");
        ERROR_CODE_MAP.put("MOBILE_IN_BLACK", "手机号在黑名单");
        ERROR_CODE_MAP.put("MOBLLE_TERMINAL_ERROR", "手机终端问题、内存满、SIM卡满、非法设备等");
        ERROR_CODE_MAP.put("CONTENT_KEYWORD", "内容关键字拦截");
        ERROR_CODE_MAP.put("INVALID_NUMBER", "号码状态异常");
        ERROR_CODE_MAP.put("CONTENT_ERROR", "内容无退订");
        ERROR_CODE_MAP.put("REQUEST_SUCCESS", "请求成功（平台接到请求，但未接收到运营商侧的回执状态）");
        ERROR_CODE_MAP.put("SP_NOT_BY_INTER_SMS", "未开通国际短信");
        ERROR_CODE_MAP.put("SP_UNKNOWN_ERROR", "运营商未知错误");
        ERROR_CODE_MAP.put("USER_REJECT", "接收用户已退订此业务或产品未开通");
        ERROR_CODE_MAP.put("NO_ROUTE", "当前短信内容无可用通道发送");

    }
    /**
     * 短信发送 ，手机号为多个以逗号隔开
     *
     * @param smsRequest
     * @return
     */
    @Override
    public List<SmsResponse> sendSms(SmsRequest smsRequest) {
        String templateParams = smsRequest.getTemplateParams();
        try {
            List<String> telNums = smsRequest.getTelNums();
            AsyncClient client = createClient(smsRequest);
            SendSmsRequest sendSmsRequest = SendSmsRequest.builder()
                    .phoneNumbers(String.join(",", telNums))
                    .signName(smsRequest.getSignName())
                    .templateCode(smsRequest.getTemplateCode())
                    .templateParam(JSON.toJSONString(parseParam(templateParams)))
                    .build();
            CompletableFuture<SendSmsResponse> sendSmsResponseCompletableFuture = client.sendSms(sendSmsRequest);
            CompletableFuture.allOf(sendSmsResponseCompletableFuture);
            SendSmsResponse sendSmsResponse = sendSmsResponseCompletableFuture.join();
            return assembleSendSmsResponse(sendSmsResponse,telNums);
        } catch (Exception e) {
            log.error("SDK:阿里云  endpoint: {} 发送短信报错信息: {}",smsRequest.getEndPoint(),e.getMessage());
            return Collections.emptyList();
        }
    }

    private List<SmsResponse> assembleSendSmsResponse(SendSmsResponse sendSmsResponse,List<String> phoneList) {
        if (sendSmsResponse == null || sendSmsResponse.getBody() == null) {
            return Collections.emptyList();
        }
        List<SmsResponse> responses = new LinkedList<>();
        SendSmsResponseBody body = sendSmsResponse.getBody();
        phoneList.forEach(phone -> responses.add(SmsResponse.builder()
                .code(body.getCode())
                .sendStatus(ProviderType.ALI.getTaskStatus(body.getCode()))
                .bizId(body.getBizId())
                .phoneNumber(phone)
                .ext(body.getRequestId())
                .message(body.getMessage())
                .build()));
        return responses;
    }
    @SneakyThrows
    public static AsyncClient createClient(SmsRequest smsRequest)  {
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(smsRequest.getAccessKey())
                .accessKeySecret(smsRequest.getAccessSecret())
                //.securityToken("<your-token>") // use STS token
                .build());

        return AsyncClient.builder()
                .region(smsRequest.getRegion())
                .credentialsProvider(provider)
                //.serviceConfiguration(Configuration.create()) // Service-level configuration
                // Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
                .overrideConfiguration(
                        ClientOverrideConfiguration.create()
                                .setEndpointOverride(smsRequest.getEndPoint())
                        //.setReadTimeout(Duration.ofSeconds(30))
                )
                .build();
    }

    @Override
    public SmsGranter getGranter(ProviderType providerType) {
        if (PROVIDER_TYPE.equals(providerType)) {
            return this;
        }
        return null;
    }
}
